home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / vfs / extfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  32.4 KB  |  1,247 lines

  1. /* Virtual File System: External file system.
  2.    Copyright (C) 1995 The Free Software Foundation
  3.    
  4.    Written by: 1995 Jakub Jelinek
  5.  
  6.    This program is free software; you can redistribute it and/or modify
  7.    it under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2 of the License, or
  9.    (at your option) any later version.
  10.  
  11.    This program is distributed in the hope that it will be useful,
  12.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with this program; if not, write to the Free Software
  18.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include <config.h>
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <fcntl.h>
  28. #include <unistd.h>
  29. #include <signal.h>
  30. #include <sys/wait.h>
  31. #include <errno.h>
  32. #include <time.h>
  33. #include "../src/fs.h"
  34. #include "../src/util.h"
  35. #include "../src/mem.h"
  36. #include "../src/mad.h"
  37. #define WANT_PARSE_LS_LGA
  38. #include "vfs.h"
  39. #include "extfs.h"
  40.  
  41. /* To not include all dialoging stuff, I'm not including dialog.h
  42.    just for this function */
  43. extern void *message (int error, char *header, char *text,...);
  44.  
  45. static struct extfs_archive *first_archive = NULL;
  46. static int extfserrno = 0;
  47. static struct stat hstat;        /* Stat struct corresponding */
  48. static char *current_file_name, *current_link_name;
  49. static char *extfs_current_dir;
  50. static struct extfs_entry *extfs_find_entry (struct extfs_entry *dir, char *name, int make_dirs);
  51.  
  52. #define MAXEXTFS 32
  53. static char *extfs_prefixes [MAXEXTFS];
  54. static char extfs_need_archive [MAXEXTFS];
  55. static int extfs_no = 0;
  56. static char *extfs_extensions [MAXEXTFS];
  57.  
  58. void extfs_fill_names (void (*func)(char *))
  59. {
  60.     struct extfs_archive *a = first_archive;
  61.     char *name;
  62.     
  63.     while (a){
  64.     name = copy_strings (extfs_prefixes [a->fstype], ":", a->name, "/",
  65.                  a->current_dir->name, 0);
  66.     (*func)(name);
  67.     free (name);
  68.     a = a->next;
  69.     }
  70. }
  71.  
  72. static void make_dot_doubledot (struct extfs_entry *ent)
  73. {
  74.     struct extfs_entry *entry = (struct extfs_entry *)
  75.     xmalloc (sizeof (struct extfs_entry), "Extfs: extfs_entry");
  76.     struct extfs_entry *parentry = ent->dir;
  77.     struct extfs_inode *inode = ent->inode, *parent;
  78.     
  79.     parent = (parentry != NULL) ? parentry->inode : NULL;
  80.     entry->name = strdup (".");
  81.     entry->has_changed = 0;
  82.     entry->inode = inode;
  83.     entry->dir = ent;
  84.     inode->local_filename = NULL;
  85.     inode->first_in_subdir = entry;
  86.     inode->last_in_subdir = entry;
  87.     inode->nlink++;
  88.     entry->next_in_dir = (struct extfs_entry *)
  89.     xmalloc (sizeof (struct extfs_entry), "Extfs: extfs_entry");
  90.     entry=entry->next_in_dir;
  91.     entry->name = strdup ("..");
  92.     entry->has_changed = 0;
  93.     inode->last_in_subdir = entry;
  94.     entry->next_in_dir = NULL;
  95.     if (parent != NULL) {
  96.         entry->inode = parent;
  97.         entry->dir = parentry;
  98.         parent->nlink++;
  99.     } else {
  100.         entry->inode = inode;
  101.         entry->dir = ent;
  102.         inode->nlink++;
  103.     }
  104. }
  105.  
  106. static struct extfs_entry *generate_entry (struct extfs_archive *archive, 
  107.     char *name, struct extfs_entry *parentry, mode_t mode)
  108. {
  109.     mode_t myumask;
  110.     struct extfs_inode *inode, *parent; 
  111.     struct extfs_entry *entry;
  112.  
  113.     parent = (parentry != NULL) ? parentry->inode : NULL;
  114.     entry = (struct extfs_entry *)
  115.     xmalloc (sizeof (struct extfs_entry), "Extfs: extfs_entry");
  116.     
  117.     entry->name = strdup (name);
  118.     entry->has_changed = 0;
  119.     entry->next_in_dir = NULL;
  120.     entry->dir = parentry;
  121.     if (parent != NULL) {
  122.         parent->last_in_subdir->next_in_dir = entry;
  123.         parent->last_in_subdir = entry;
  124.     }
  125.     inode = (struct extfs_inode *)
  126.     xmalloc (sizeof (struct extfs_inode), "Extfs: extfs_inode");
  127.     entry->inode = inode;
  128.     inode->has_changed = 0;
  129.     inode->local_filename = NULL;
  130.     inode->linkname = 0;
  131.     inode->inode = (archive->__inode_counter)++;
  132.     inode->dev = archive->rdev;
  133.     inode->archive = archive;
  134.     myumask = umask (022);
  135.     umask (myumask);
  136.     inode->mode = mode & ~myumask;
  137.     mode = inode->mode;
  138.     inode->rdev = 0;
  139.     inode->uid = getuid ();
  140.     inode->gid = getgid ();
  141.     inode->size = 0;
  142.     inode->mtime = time (NULL);
  143.     inode->atime = inode->mtime;
  144.     inode->ctime = inode->mtime;
  145.     inode->nlink = 1;
  146.     if (S_ISDIR (mode))
  147.         make_dot_doubledot (entry);
  148.     return entry;
  149. }
  150.  
  151. static void free_entries (struct extfs_entry *entry)
  152. {
  153.     return;
  154. }
  155.  
  156. static void free_archive (struct extfs_archive *archive)
  157. {
  158.     free_entries (archive->root_entry);
  159.     if (archive->local_name != NULL) {
  160.         struct stat my;
  161.         
  162.         mc_stat (archive->local_name, &my);
  163.         mc_ungetlocalcopy (archive->name, archive->local_name, 
  164.             archive->local_stat.st_mtime != my.st_mtime);
  165.         /* ungetlocalcopy frees local_name for us */
  166.     }
  167.     free (archive->name);
  168.     free (archive);
  169. }
  170.  
  171. static FILE *open_extfs_archive (int fstype, char *name, struct extfs_archive **pparc)
  172. {
  173.     static dev_t __extfs_no = 0;
  174.     FILE *result;
  175.     mode_t mode;
  176.     char *cmd;
  177.     struct stat mystat;
  178.     struct extfs_archive *current_archive;
  179.     struct extfs_entry *root_entry;
  180.     char *local_name = NULL;
  181.     const int uses_archive = extfs_need_archive [fstype];
  182.     
  183.     if (uses_archive){
  184.     if (mc_stat (name, &mystat) == -1)
  185.         return NULL;
  186.     if (!vfs_file_is_local (name)) {
  187.         local_name = mc_getlocalcopy (name);
  188.         if (local_name == NULL)
  189.         return NULL;
  190.     }
  191.     }
  192.     
  193.     cmd = copy_strings (LIBDIR "extfs/", extfs_prefixes [fstype], 
  194.                         " list ", local_name ? local_name : name, 0);
  195.     result = popen (cmd, "r");
  196.     free (cmd);
  197.     if (result == NULL) {
  198.         if (local_name != NULL && uses_archive)
  199.             mc_ungetlocalcopy (name, local_name, 0);
  200.         return NULL;
  201.     }
  202.     
  203.     current_archive = (struct extfs_archive *)
  204.                       xmalloc (sizeof (struct extfs_archive), "Tar archive");
  205.     current_archive->fstype = fstype;
  206.     current_archive->name = name ? strdup (name): name;
  207.     current_archive->local_name = local_name;
  208.     
  209.     if (local_name != NULL)
  210.         mc_stat (local_name, ¤t_archive->local_stat);
  211.     current_archive->__inode_counter = 0;
  212.     current_archive->fd_usage = 0;
  213.     current_archive->extfsstat = mystat;
  214.     current_archive->rdev = __extfs_no++;
  215.     current_archive->next = first_archive;
  216.     first_archive = current_archive;
  217.     mode = current_archive->extfsstat.st_mode & 07777;
  218.     if (mode & 0400)
  219.         mode |= 0100;
  220.     if (mode & 0040)
  221.         mode |= 0010;
  222.     if (mode & 0004)
  223.         mode |= 0001;
  224.     mode |= S_IFDIR;
  225.     root_entry = generate_entry (current_archive, "/", NULL, mode);
  226.     root_entry->inode->uid = current_archive->extfsstat.st_uid;
  227.     root_entry->inode->gid = current_archive->extfsstat.st_gid;
  228.     root_entry->inode->atime = current_archive->extfsstat.st_atime;
  229.     root_entry->inode->ctime = current_archive->extfsstat.st_ctime;
  230.     root_entry->inode->mtime = current_archive->extfsstat.st_mtime;
  231.     current_archive->root_entry = root_entry;
  232.     current_archive->current_dir = root_entry;
  233.     
  234.     *pparc = current_archive;
  235.  
  236.     return result;
  237. }
  238.  
  239. /*
  240.  * Main loop for reading an archive.
  241.  * Returns 0 on success, -1 on error.
  242.  */
  243. int read_extfs_archive (int fstype, char *name, struct extfs_archive **pparc)
  244. {
  245.     FILE *extfsd;
  246.     char *buffer;
  247.     struct extfs_archive *current_archive;
  248.  
  249.     if ((extfsd = open_extfs_archive (fstype, name, ¤t_archive)) == NULL) {
  250.         message (1, " Error ", "Couldn't open %s archive\n%s", 
  251.             extfs_prefixes [fstype], name);
  252.     return -1;
  253.     }
  254.  
  255.     buffer = xmalloc (4096, "Extfs: buffer");
  256.     while (fgets (buffer, 4096, extfsd) != NULL) {
  257.         current_link_name = NULL;
  258.         if (parse_ls_lga (buffer, &hstat, ¤t_file_name, ¤t_link_name)) {
  259.             struct extfs_entry *entry, *pent;
  260.             struct extfs_inode *inode;
  261.             char *p, *q, *cfn = current_file_name;
  262.             
  263.             if (*cfn) {
  264.                 if (*cfn == '/')
  265.                     cfn++;
  266.                 p = strchr (cfn, 0);
  267.                 if (p != NULL && p != cfn && *(p - 1) == '/')
  268.                     *(p - 1) = 0;
  269.                 p = strrchr (cfn, '/');
  270.                 if (p == NULL) {
  271.                     p = cfn;
  272.                     q = strchr (cfn, 0);
  273.                 } else {
  274.                     *(p++) = 0;
  275.                     q = cfn;
  276.                 }
  277.                 if (S_ISDIR (hstat.st_mode) && 
  278.                     (!strcmp (p, ".") || !strcmp (p, "..")))
  279.                     goto read_extfs_continue;
  280.                 pent = extfs_find_entry (current_archive->root_entry, q, 1);
  281.                 if (pent == NULL) {
  282.                     message (1, " Error ", "Inconsistent extfs archive");
  283.                     /* FIXME: Should clean everything one day */
  284.                     return -1;
  285.                 }
  286.         entry = (struct extfs_entry *) xmalloc (sizeof (struct extfs_entry), "Extfs: extfs_entry");
  287.         entry->name = strdup (p);
  288.         entry->has_changed = 0;
  289.         entry->next_in_dir = NULL;
  290.         entry->dir = pent;
  291.         if (pent != NULL) {
  292.                 pent->inode->last_in_subdir->next_in_dir = entry;
  293.                 pent->inode->last_in_subdir = entry;
  294.         }
  295.         if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
  296.                 pent = extfs_find_entry (current_archive->root_entry, current_link_name, 0);
  297.                 if (pent == NULL) {
  298.                     message (1, " Error ", "Inconsistent extfs archive");
  299.                     /* FIXME: Should clean everything one day */
  300.                     return -1;
  301.                 } else {
  302.             entry->inode = pent->inode;
  303.             pent->inode->nlink++;
  304.                 }
  305.             } else {
  306.             inode = (struct extfs_inode *) xmalloc (sizeof (struct extfs_inode), "Extfs: extfs_inode");
  307.             entry->inode = inode;
  308.             inode->local_filename = NULL;
  309.             inode->has_changed = 0;
  310.             inode->inode = (current_archive->__inode_counter)++;
  311.             inode->nlink = 1;
  312.             inode->dev = current_archive->rdev;
  313.             inode->archive = current_archive;
  314.             inode->mode = hstat.st_mode;
  315. #ifdef HAVE_ST_RDEV            
  316.             inode->rdev = hstat.st_rdev;
  317. #else
  318.             inode->rdev = 0;
  319. #endif            
  320.             inode->uid = hstat.st_uid;
  321.             inode->gid = hstat.st_gid;
  322.             inode->size = hstat.st_size;
  323.             inode->mtime = hstat.st_mtime;
  324.             inode->atime = hstat.st_atime;
  325.             inode->ctime = hstat.st_ctime;
  326.             if (current_link_name != NULL && S_ISLNK (hstat.st_mode)) {
  327.                 inode->linkname = current_link_name;
  328.                 current_link_name = NULL;
  329.             } else
  330.                 inode->linkname = NULL;
  331.             if (S_ISDIR (hstat.st_mode))
  332.                 make_dot_doubledot (entry);
  333.             }
  334.             }
  335.  read_extfs_continue:
  336.             free (current_file_name);
  337.             if (current_link_name != NULL)
  338.                 free (current_link_name);
  339.         }
  340.     }
  341.     pclose (extfsd);
  342.     *pparc = current_archive;
  343.     return 0;
  344. }
  345.  
  346. char *extfs_analysis (char *name, char **archive, int *fstype, int is_dir)
  347. {
  348.     char *p, *local, *q;
  349.     int j;
  350.     char *archive_name = NULL;
  351.  
  352.                                                /*  |  this is len of prefix plus some minimum
  353.                                                 *  v  space needed for the extension */
  354.     *fstype = extfs_prefix_to_type (name);
  355.     if (*fstype == -1) {
  356.         extfserrno = ENOENT;
  357.         return NULL;
  358.     }
  359.  
  360.     if (!extfs_need_archive [*fstype]){
  361.     *archive = 0;
  362.     return strdup (name + strlen(extfs_prefixes [*fstype]) + 1);
  363.     }
  364.     
  365.     for (p = name + strlen (name); p > name; p--)
  366.         if (*p == '/' || (is_dir && !*p))
  367.             for (q = extfs_extensions [*fstype]; *q; q += j + 1) {
  368.                 j = strlen (q);
  369.                 if (p - j < name + 1)
  370.                     continue;
  371.                 if (!strncmp (p - j, q, j)) {
  372.                     char c = *p;
  373.                     
  374.                     *p = 0;
  375.                     archive_name = 
  376.                         vfs_canon (name + strlen (extfs_prefixes [*fstype]) + 1);
  377.                     *archive = archive_name;
  378.                     *p = c;
  379.                     local = strdup (p);
  380.                     return local;
  381.                 }
  382.             }
  383.     extfserrno = ENOENT;
  384.     return NULL;
  385. }
  386.  
  387. /* Returns allocated path inside the archive or NULL */
  388. static char *extfs_get_path (char *inname, struct extfs_archive **archive, int is_dir,
  389.     int do_not_open)
  390. {
  391.     char *local, *archive_name;
  392.     int result = -1;
  393.     struct extfs_archive *parc;
  394.     struct vfs_stamping *parent;
  395.     vfs *v;
  396.     int fstype;
  397.     
  398.     local = extfs_analysis (inname, &archive_name, &fstype, is_dir);
  399.     if (local == NULL) {
  400.         extfserrno = ENOENT;
  401.         return NULL;
  402.     }
  403.  
  404.     /* In the case of the file systems that do not require a local
  405.      * archive, we compare the fstype instead of the archive name,
  406.      * of course, this has the drawback that we can't have more than
  407.      * one external file system for each prefix.
  408.      *
  409.      * Actually, we should implement an alias mechanism that would
  410.      * translate: "a:" to "dos:a.
  411.      *
  412.      */
  413.     for (parc = first_archive; parc != NULL; parc = parc->next)
  414.     if (archive_name){
  415.         if (!strcmp (parc->name, archive_name)) {
  416.         vfs_stamp (&extfs_vfs_ops, (vfsid) parc);
  417.         goto return_success;
  418.         }
  419.     } else {
  420.         if (parc->fstype == fstype){
  421.         vfs_stamp (&extfs_vfs_ops, (vfsid) parc);
  422.         goto return_success;
  423.         }
  424.     }
  425.         
  426.     if (do_not_open)
  427.         result = -1;
  428.     else
  429.         result = read_extfs_archive (fstype, archive_name, &parc);
  430.     if (result == -1) {
  431.     extfserrno = EIO;
  432.     free(local);
  433.     if (archive_name)
  434.         free(archive_name);
  435.     return NULL;
  436.     }
  437.     if (archive_name){
  438.     v = vfs_type (archive_name);
  439.     if (v == &local_vfs_ops) {
  440.         parent = NULL;
  441.     } else {
  442.         parent = xmalloc (sizeof (struct vfs_stamping), "vfs stamping");
  443.         parent->v = v;
  444.         parent->id = (*v->getid) (archive_name, &(parent->parent));
  445.     }
  446.     vfs_add_noncurrent_stamps (&extfs_vfs_ops, (vfsid) parc, parent);
  447.     }
  448.  return_success:
  449.     *archive = parc;
  450.     if (archive_name)
  451.     free (archive_name);
  452.     return local;
  453. }
  454.  
  455. struct extfs_loop_protect {
  456.     struct extfs_entry *entry;
  457.     struct extfs_loop_protect *next;
  458. };
  459. static int errloop;
  460. static int notadir;
  461.  
  462. static struct extfs_entry *
  463. __extfs_find_entry (struct extfs_entry *dir, char *name,
  464.             struct extfs_loop_protect *list, int make_dirs);
  465.  
  466. static struct extfs_entry *
  467. __extfs_resolve_symlinks (struct extfs_entry *entry, 
  468.               struct extfs_loop_protect *list)
  469. {
  470.     struct extfs_entry *pent;
  471.     struct extfs_loop_protect *looping;
  472.     
  473.     if (!S_ISLNK (entry->inode->mode))
  474.         return entry;
  475.     for (looping = list; looping != NULL; looping = looping->next)
  476.         if (entry == looping->entry) { /* Here we protect us against symlink looping */
  477.             errloop = 1;
  478.             return NULL;
  479.         }
  480.     looping = (struct extfs_loop_protect *)
  481.     xmalloc (sizeof (struct extfs_loop_protect), 
  482.          "Extfs: symlink looping protection");
  483.     looping->entry = entry;
  484.     looping->next = list;
  485.     pent = __extfs_find_entry (entry->dir, entry->inode->linkname, looping, 0);
  486.     free (looping);
  487.     if (pent == NULL)
  488.         extfserrno = ENOENT;
  489.     return pent;
  490. }
  491.  
  492. static struct extfs_entry *extfs_resolve_symlinks (struct extfs_entry *entry)
  493. {
  494.     struct extfs_entry *res;
  495.     
  496.     errloop = 0;
  497.     notadir = 0;
  498.     res = __extfs_resolve_symlinks (entry, NULL);
  499.     if (res == NULL) {
  500.         if (errloop)
  501.             extfserrno = ELOOP;
  502.         else if (notadir)
  503.             extfserrno = ENOTDIR;
  504.     }
  505.     return res;
  506. }
  507.  
  508. static struct extfs_entry*
  509. __extfs_find_entry (struct extfs_entry *dir, char *name, 
  510.              struct extfs_loop_protect *list, int make_dirs)
  511. {
  512.     struct extfs_entry *pent, *pdir;
  513.     char *p, *q, *name_end;
  514.     char c;
  515.  
  516.     if (*name == '/') { /* Handle absolute paths */
  517.         name++;
  518.         dir = dir->inode->archive->root_entry;
  519.     }
  520.  
  521.     pent = dir;
  522.     p = name;
  523.     name_end = name + strlen (name);
  524.     q = strchr (p, '/');
  525.     c = '/';
  526.     if (!q)
  527.     q = strchr (p, 0);
  528.     
  529.     for (; pent != NULL && c && *p; ){
  530.     c = *q;
  531.     *q = 0;
  532.  
  533.     if (strcmp (p, ".")){
  534.         if (!strcmp (p, "..")) 
  535.         pent = pent->dir;
  536.         else {
  537.         if ((pent = __extfs_resolve_symlinks (pent, list))==NULL){
  538.             *q = c;
  539.             return NULL;
  540.         }
  541.         if (c == '/' && !S_ISDIR (pent->inode->mode)){
  542.             *q = c;
  543.             notadir = 1;
  544.             return NULL;
  545.         }
  546.         pdir = pent;
  547.         for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
  548.             /* Hack: I keep the original semanthic unless
  549.                q+1 would break in the strchr */
  550.             if (!strcmp (pent->name, p)){
  551.             if (q + 1 > name_end){
  552.                 *q = c;
  553.                 notadir = !S_ISDIR (pent->inode->mode);
  554.                 return pent;
  555.             }
  556.             break;
  557.             }
  558.         
  559.         /* When we load archive, we create automagically
  560.          * non-existant directories
  561.          */
  562.         if (pent == NULL && make_dirs) { 
  563.             pent = generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
  564.         }
  565.         }
  566.     }
  567.     /* Next iteration */
  568.     *q = c;
  569.     p = q + 1;
  570.     q = strchr (p, '/');
  571.     if (!q)
  572.         q = strchr (p, 0);
  573.     }
  574.     if (pent == NULL)
  575.         extfserrno = ENOENT;
  576.     return pent;
  577. }
  578.  
  579. static struct extfs_entry *extfs_find_entry (struct extfs_entry *dir, char *name, int make_dirs)
  580. {
  581.     struct extfs_entry *res;
  582.     
  583.     errloop = 0;
  584.     notadir = 0;
  585.     res = __extfs_find_entry (dir, name, NULL, make_dirs);
  586.     if (res == NULL) {
  587.         if (errloop)
  588.             extfserrno = ELOOP;
  589.         else if (notadir)
  590.             extfserrno = ENOTDIR;
  591.     }
  592.     return res;
  593. }
  594.  
  595. struct extfs_pseudofile {
  596.     struct extfs_archive *archive;
  597.     unsigned int has_changed:1;
  598.     int local_handle;
  599.     struct extfs_entry *entry;
  600. };
  601.  
  602. static char *get_archive_name (struct extfs_archive *archive)
  603. {
  604.     char *archive_name;
  605.  
  606.     if (archive->local_name)
  607.     archive_name = archive->local_name;
  608.     else
  609.     archive_name = archive->name;
  610.     
  611.     if (!archive_name || !*archive_name)
  612.     return "no_archive_name";
  613.     else
  614.     return archive_name;
  615. }
  616.  
  617. void extfs_run (char *file)
  618. {
  619.     struct extfs_archive *archive;
  620.     char *p, *q, *cmd;
  621.     struct extfs_entry *entry;
  622.  
  623.     if ((p = extfs_get_path (file, &archive, 0, 0)) == NULL)
  624.     return;
  625.     q = (*p == '/') ? p + 1 : p;
  626.     
  627.     cmd = copy_strings (LIBDIR "extfs/", extfs_prefixes [archive->fstype], 
  628.                         " run ", get_archive_name(archive), " ", q, 0);
  629.     shell_execute(cmd, 0);
  630.     free(cmd);
  631.     free(p);
  632. }
  633.  
  634. static void *extfs_open (char *file, int flags, int mode)
  635. {
  636.     struct extfs_pseudofile *extfs_info;
  637.     struct extfs_archive *archive;
  638.     char *p, *q;
  639.     struct extfs_entry *entry;
  640.     int local_handle;
  641.  
  642.     if ((p = extfs_get_path (file, &archive, 0, 0)) == NULL)
  643.     return NULL;
  644.     q = (*p == '/') ? p + 1 : p;
  645.     entry = extfs_find_entry (archive->root_entry, q, 0);
  646.     if (entry == NULL) {
  647.         free (p);
  648.         return NULL;
  649.     }
  650.     if ((entry = extfs_resolve_symlinks (entry)) == NULL) {
  651.         free (p);
  652.     return NULL;
  653.     }
  654.     if (S_ISDIR (entry->inode->mode)) {
  655.         extfserrno = EISDIR;
  656.         free (p);
  657.         return NULL;
  658.     }
  659.     if (entry->inode->local_filename == NULL) {
  660.         char *cmd;
  661.         
  662.         entry->inode->local_filename = strdup (tmpnam (NULL));
  663.         cmd = copy_strings (LIBDIR "extfs/", extfs_prefixes [archive->fstype], 
  664.                             " copyout ", 
  665.                             get_archive_name (archive), 
  666.                             " ", q, " ", entry->inode->local_filename, 0);
  667.         if (system (cmd)) {
  668.             free (entry->inode->local_filename);
  669.             entry->inode->local_filename = NULL;
  670.             free (cmd);
  671.             extfserrno = EIO;
  672.             return NULL;
  673.         }
  674.         free (cmd);
  675.     }
  676.     free (p);
  677.     
  678.     local_handle = open (entry->inode->local_filename, flags, mode);
  679.     if (local_handle == -1) {
  680.         extfserrno = EIO;
  681.         return NULL;
  682.     }
  683.     
  684.     extfs_info = (struct extfs_pseudofile *) xmalloc (sizeof (struct extfs_pseudofile), "Extfs: extfs_open");
  685.     extfs_info->archive = archive;
  686.     extfs_info->entry = entry;
  687.     extfs_info->has_changed = 0;
  688.     extfs_info->local_handle = local_handle;
  689.  
  690.     /* i.e. we had no open files and now we have one */
  691.     vfs_rmstamp (&extfs_vfs_ops, (vfsid) archive, 1);
  692.     archive->fd_usage++;
  693.     return extfs_info;
  694. }
  695.  
  696. static int extfs_read (void *data, char *buffer, int count)
  697. {
  698.     struct extfs_pseudofile *file = (struct extfs_pseudofile *)data;
  699.  
  700.     return read (file->local_handle, buffer, count);
  701. }
  702.  
  703. static int extfs_close (void *data)
  704. {
  705.     struct extfs_pseudofile *file;
  706.     int    errno_code = 0;
  707.     file = (struct extfs_pseudofile *)data;
  708.     
  709.     close (file->local_handle);
  710.  
  711.     /* Commit the file if it has changed */
  712.     if (file->has_changed){
  713.     struct extfs_archive *archive;
  714.     char   *archive_name;
  715.     char   *cmd;
  716.     
  717.     archive = file->archive;
  718.     archive_name = get_archive_name (archive);
  719.     
  720.     cmd = copy_strings (LIBDIR "extfs/",
  721.                 extfs_prefixes [archive->fstype],
  722.                 " copyin ", archive_name, " ",
  723.                 file->entry->name, " ",
  724.                 file->entry->inode->local_filename, 0);
  725.     if (system (cmd))
  726.         errno_code = EIO;
  727.     }
  728.     
  729.     file->archive->fd_usage--;
  730.     if (!file->archive->fd_usage) {
  731.         struct vfs_stamping *parent;
  732.         vfs *v;
  733.         
  734.     if (!file->archive->name || !*file->archive->name || (v = vfs_type (file->archive->name)) == &local_vfs_ops) {
  735.         parent = NULL;
  736.     } else {
  737.         parent = xmalloc (sizeof (struct vfs_stamping), "vfs stamping");
  738.         parent->v = v;
  739.         parent->id = (*v->getid) (file->archive->name, &(parent->parent));
  740.     }
  741.         vfs_add_noncurrent_stamps (&extfs_vfs_ops, (vfsid) (file->archive), parent);
  742.     }
  743.     
  744.     free (data);
  745.     if (errno_code){
  746.     extfserrno = EIO;    /* Non standard, but who cares? */
  747.     return -1;
  748.     } else
  749.     return 0;
  750. }
  751.  
  752. static int extfs_errno (void)
  753. {
  754.     return extfserrno;
  755. }
  756.  
  757. static void *extfs_opendir (char *dirname)
  758. {
  759.     struct extfs_archive *archive;
  760.     char *p, *q;
  761.     struct extfs_entry *entry;
  762.     struct extfs_entry **extfs_info;
  763.  
  764.     if ((p = extfs_get_path (dirname, &archive, 1, 0)) == NULL)
  765.     return NULL;
  766.     q = (*p == '/') ? p + 1 : p;
  767.     entry = extfs_find_entry (archive->root_entry, q, 0);
  768.     free (p);
  769.     if (entry == NULL)
  770.         return NULL;
  771.     if ((entry = extfs_resolve_symlinks (entry)) == NULL)
  772.     return NULL;
  773.     if (!S_ISDIR (entry->inode->mode)) {
  774.         extfserrno = ENOTDIR;
  775.         return NULL;
  776.     }
  777.  
  778.     extfs_info = (struct extfs_entry **) xmalloc (sizeof (struct extfs_entry *), "Extfs: extfs_opendir");
  779.     *extfs_info = entry->inode->first_in_subdir;
  780.  
  781.     return extfs_info;
  782. }
  783.  
  784. static void *extfs_readdir (void *data)
  785. {
  786.     static struct {
  787.     struct dirent dir;
  788. #ifdef NEED_EXTRA_DIRENT_BUFFER
  789.     char extra_buffer [MC_MAXPATHLEN];
  790. #endif
  791.     } dir;
  792.     
  793.     struct extfs_entry **extfs_info = (struct extfs_entry **) data;
  794.     
  795.     if (*extfs_info == NULL)
  796.         return NULL;
  797.     
  798.     strcpy (&(dir.dir.d_name [0]), (*extfs_info)->name);
  799.     
  800. #ifndef DIRENT_LENGTH_COMPUTED
  801.     dir.d_namlen = strlen (dir.dir.d_name);
  802. #endif
  803.     *extfs_info = (*extfs_info)->next_in_dir;
  804.     
  805.     return (void *)&dir;
  806. }
  807.  
  808. static int extfs_closedir (void *data)
  809. {
  810.     free (data);
  811.     return 0;
  812. }
  813.  
  814. static int _extfs_stat (char *path, struct stat *buf, int resolve)
  815. {
  816.     struct extfs_archive *archive;
  817.     char *p, *q;
  818.     struct extfs_entry *entry;
  819.     struct extfs_inode *inode;
  820.  
  821.     if ((p = extfs_get_path (path, &archive, 0, 0)) == NULL)
  822.     return -1;
  823.     q = (*p == '/') ? p + 1 : p;
  824.     entry = extfs_find_entry (archive->root_entry, q, 0);
  825.     free (p);
  826.     if (entry == NULL)
  827.         return -1;
  828.     if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
  829.     return -1;
  830.     inode = entry->inode;
  831.     buf->st_dev = inode->dev;
  832.     buf->st_ino = inode->inode;
  833.     buf->st_mode = inode->mode;
  834.     buf->st_nlink = inode->nlink;
  835.     buf->st_uid = inode->uid;
  836.     buf->st_gid = inode->gid;
  837. #ifdef HAVE_ST_RDEV
  838.     buf->st_rdev = inode->rdev;
  839. #endif
  840.     buf->st_size = inode->size;
  841. #ifdef HAVE_ST_BLKSIZE
  842.     buf->st_blksize = 512;
  843. #endif
  844. #ifdef HAVE_ST_BLOCKS
  845.     buf->st_blocks = (inode->size + 512 - 1) / 512;
  846. #endif
  847.     buf->st_atime = inode->atime;
  848.     buf->st_mtime = inode->mtime;
  849.     buf->st_ctime = inode->ctime;
  850.     return 0;
  851. }
  852.  
  853. static int extfs_stat (char *path, struct stat *buf)
  854. {
  855.     return _extfs_stat (path, buf, 1);
  856. }
  857.  
  858. static int extfs_lstat (char *path, struct stat *buf)
  859. {
  860.     return _extfs_stat (path, buf, 0);
  861. }
  862.  
  863. static int extfs_fstat (void *data, struct stat *buf)
  864. {
  865.     struct extfs_pseudofile *file = (struct extfs_pseudofile *)data;
  866.     struct extfs_inode *inode;
  867.     
  868.     inode = file->entry->inode;
  869.     buf->st_dev = inode->dev;
  870.     buf->st_ino = inode->inode;
  871.     buf->st_mode = inode->mode;
  872.     buf->st_nlink = inode->nlink;
  873.     buf->st_uid = inode->uid;
  874.     buf->st_gid = inode->gid;
  875. #ifdef HAVE_ST_RDEV    
  876.     buf->st_rdev = inode->rdev;
  877. #endif
  878.     buf->st_size = inode->size;
  879. #ifdef HAVE_ST_BLKSIZE
  880.     buf->st_blksize = 512;
  881. #endif
  882. #ifdef HAVE_ST_BLOCKS
  883.     buf->st_blocks = (inode->size + 512 - 1) / 512;
  884. #endif
  885.     buf->st_atime = inode->atime;
  886.     buf->st_mtime = inode->mtime;
  887.     buf->st_ctime = inode->ctime;
  888.     return 0;
  889. }
  890.  
  891. static int extfs_chmod (char *path, int mode)
  892. {
  893.     return -1;
  894. }
  895.  
  896. static int extfs_chown (char *path, int owner, int group)
  897. {
  898.     return -1;
  899. }
  900.  
  901. static int extfs_readlink (char *path, char *buf, int size)
  902. {
  903.     struct extfs_archive *archive;
  904.     char *p, *q;
  905.     int i;
  906.     struct extfs_entry *entry;
  907.  
  908.     if ((p = extfs_get_path (path, &archive, 0, 0)) == NULL)
  909.     return -1;
  910.     q = (*p == '/') ? p + 1 : p;
  911.     entry = extfs_find_entry (archive->root_entry, q, 0);
  912.     free (p);
  913.     if (entry == NULL)
  914.         return -1;
  915.     if (!S_ISLNK (entry->inode->mode)) {
  916.         extfserrno = EINVAL;
  917.         return -1;
  918.     }
  919.     if (size > (i = strlen (entry->inode->linkname))) {
  920.         size = i;
  921.     }
  922.     strncpy (buf, entry->inode->linkname, i);
  923.     return i;
  924. }
  925.  
  926. static int extfs_unlink (char *path)
  927. {
  928.     return -1;
  929. }
  930.  
  931. static int extfs_symlink (char *n1, char *n2)
  932. {
  933.     return -1;
  934. }
  935.  
  936. static int extfs_write (void *data, char *buf, int nbyte)
  937. {
  938.     struct extfs_pseudofile *file = (struct extfs_pseudofile *)data;
  939.  
  940.     file->has_changed = 1;
  941.     return write (file->local_handle, buf, nbyte);
  942. }
  943.  
  944. static int extfs_rename (char *a, char *b)
  945. {
  946.     return -1;
  947. }
  948.  
  949. static int extfs_chdir (char *path)
  950. {
  951.     struct extfs_archive *archive;
  952.     char *p, *q, *res;
  953.     struct extfs_entry *entry;
  954.  
  955.     extfserrno = ENOTDIR;
  956.     if ((p = extfs_get_path (path, &archive, 1, 0)) == NULL)
  957.     return -1;
  958.     q = (*p == '/') ? p + 1 : p;
  959.     entry = extfs_find_entry (archive->root_entry, q, 0);
  960.     if (entry == NULL) {
  961.         free (p);
  962.         return -1;
  963.     }
  964.     entry = extfs_resolve_symlinks (entry);
  965.     if (entry == NULL) {
  966.         free (p);
  967.         return -1;
  968.     }
  969.     if (!S_ISDIR (entry->inode->mode)) {
  970.         free (p);
  971.         return -1;
  972.     }
  973.     entry->inode->archive->current_dir = entry;
  974.     res = copy_strings (extfs_prefixes [entry->inode->archive->fstype], ":",
  975.         entry->inode->archive->name, p, NULL);
  976.     free (p);
  977.     extfserrno = 0;
  978.     if (extfs_current_dir)
  979.     free (extfs_current_dir);
  980.     extfs_current_dir = res;
  981.     return 0;
  982. }
  983.  
  984. static int extfs_lseek (void *data, off_t offset, int whence)
  985. {
  986.     struct extfs_pseudofile *file = (struct extfs_pseudofile *) data;
  987.  
  988.     return lseek (file->local_handle, offset, whence);
  989. }
  990.  
  991. static int extfs_mknod (char *path, int mode, int dev)
  992. {
  993.     return -1;
  994. }
  995.  
  996. static int extfs_link (char *p1, char *p2)
  997. {
  998.     return -1;
  999. }
  1000.  
  1001. static int extfs_mkdir (char *path, mode_t mode)
  1002. {
  1003.     return -1;
  1004. }
  1005.  
  1006. static int extfs_rmdir (char *path)
  1007. {
  1008.     return -1;
  1009. }
  1010.  
  1011. static vfsid extfs_getid (char *path, struct vfs_stamping **parent)
  1012. {
  1013.     struct extfs_archive *archive;
  1014.     vfs *v;
  1015.     vfsid id;
  1016.     char *p;
  1017.     struct vfs_stamping *par;
  1018.  
  1019.     *parent = NULL;
  1020.     if ((p = extfs_get_path (path, &archive, 1, 1)) == NULL) {
  1021.     return (vfsid) -1;
  1022.     }
  1023.     free (p);
  1024.     if (archive->name){
  1025.     v = vfs_type (archive->name);
  1026.     id = (*v->getid) (archive->name, &par);
  1027.     if (id != (vfsid)-1) {
  1028.         *parent = xmalloc (sizeof (struct vfs_stamping), "vfs stamping");
  1029.         (*parent)->v = v;
  1030.         (*parent)->id = id;
  1031.         (*parent)->parent = par;
  1032.         (*parent)->next = NULL;
  1033.     }
  1034.     }
  1035.     return (vfsid) archive;    
  1036. }
  1037.  
  1038. static int extfs_nothingisopen (vfsid id)
  1039. {
  1040.     if (((struct extfs_archive *)id)->fd_usage <= 0)
  1041.         return 1;
  1042.     else
  1043.         return 0;
  1044. }
  1045.  
  1046. static void free_entry (struct extfs_entry *e)
  1047. {
  1048.     int i = --(e->inode->nlink);
  1049.     if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
  1050.         struct extfs_entry *f = e->inode->first_in_subdir;
  1051.         
  1052.         e->inode->first_in_subdir = NULL;
  1053.         free_entry (f);
  1054.     }
  1055.     if (i <= 0) {
  1056.         if (e->inode->local_filename != NULL) {
  1057.             unlink (e->inode->local_filename);
  1058.             free (e->inode->local_filename);
  1059.         }
  1060.         if (e->inode->linkname != NULL)
  1061.             free (e->inode->linkname);
  1062.         free (e->inode);
  1063.     }
  1064.     if (e->next_in_dir != NULL)
  1065.         free_entry (e->next_in_dir);
  1066.     free (e->name);
  1067.     free (e);
  1068. }
  1069.  
  1070. static void extfs_free (vfsid id)
  1071. {
  1072.     struct extfs_archive *parc;
  1073.     struct extfs_archive *archive = (struct extfs_archive *)id;
  1074.  
  1075.     free_entry (archive->root_entry);
  1076.     if (archive == first_archive) {
  1077.         first_archive = archive->next;
  1078.     } else {
  1079.         for (parc = first_archive; parc != NULL; parc = parc->next)
  1080.             if (parc->next == archive)
  1081.                 break;
  1082.         if (parc != NULL)
  1083.             parc->next = archive->next;
  1084.     }
  1085.     free_archive (archive);
  1086. }
  1087.  
  1088. static char *extfs_getlocalcopy (char *path)
  1089. {
  1090.     struct extfs_pseudofile *fp = 
  1091.         (struct extfs_pseudofile *) extfs_open (path, O_RDONLY, 0);
  1092.     char *p;
  1093.     
  1094.     if (fp == NULL)
  1095.         return NULL;
  1096.     if (fp->entry->inode->local_filename == NULL) {
  1097.         extfs_close ((void *) fp);
  1098.         return NULL;
  1099.     }
  1100.     p = strdup (fp->entry->inode->local_filename);
  1101.     fp->archive->fd_usage++;
  1102.     extfs_close ((void *) fp);
  1103.     return p;
  1104. }
  1105.  
  1106. static void extfs_ungetlocalcopy (char *path, char *local, int has_changed)
  1107. {
  1108.     struct extfs_pseudofile *fp = 
  1109.         (struct extfs_pseudofile *) extfs_open (path, O_WRONLY, 0);
  1110.     
  1111.     if (fp == NULL)
  1112.         return;
  1113.     if (!strcmp (fp->entry->inode->local_filename, local)) {
  1114.         fp->entry->inode->has_changed = has_changed;
  1115.         fp->archive->fd_usage--;
  1116.         extfs_close ((void *) fp);
  1117.         return;
  1118.     } else {
  1119.         /* Should not happen */
  1120.         extfs_close ((void *) fp);
  1121.         mc_def_ungetlocalcopy (path, local, has_changed);
  1122.     }
  1123. }
  1124.  
  1125. #ifdef HAVE_MMAP
  1126. caddr_t extfs_mmap (caddr_t addr, size_t len, int prot, int flags, void *data, off_t offset)
  1127. {
  1128.     return (caddr_t)-1;
  1129. }
  1130.  
  1131. int extfs_munmap (caddr_t addr, size_t len, void *data)
  1132. {
  1133.     return -1;
  1134. }
  1135. #endif
  1136.  
  1137. vfs extfs_vfs_ops =
  1138. {
  1139.     extfs_open,
  1140.     extfs_close,
  1141.     extfs_read,
  1142.     extfs_write,
  1143.  
  1144.     extfs_opendir,
  1145.     extfs_readdir,
  1146.     extfs_closedir,
  1147.  
  1148.     extfs_stat,
  1149.     extfs_lstat,
  1150.     extfs_fstat,
  1151.  
  1152.     extfs_chmod,        /* unimplemented */
  1153.     extfs_chown,        /* unimplemented */
  1154.  
  1155.     extfs_readlink,
  1156.     
  1157.     extfs_symlink,        /* unimplemented */
  1158.     extfs_link,            /* unimplemented */
  1159.     extfs_unlink,        /* unimplemented */
  1160.  
  1161.     extfs_rename,        /* unimplemented */
  1162.     extfs_chdir,
  1163.     extfs_errno,
  1164.     extfs_lseek,
  1165.     extfs_mknod,        /* unimplemented */
  1166.     
  1167.     extfs_getid,
  1168.     extfs_nothingisopen,
  1169.     extfs_free,
  1170.     
  1171.     extfs_getlocalcopy,
  1172.     extfs_ungetlocalcopy,
  1173.     
  1174.     extfs_mkdir,        /* unimplemented */
  1175.     extfs_rmdir,        /* unimplemented */
  1176.     NULL,
  1177.     NULL
  1178. #ifdef HAVE_MMAP
  1179.     , extfs_mmap,
  1180.     extfs_munmap
  1181. #endif    
  1182. };
  1183.  
  1184. #include "../src/profile.h"
  1185. void extfs_init (void)
  1186. {
  1187.     void *keys = profile_init_iterator ("extfs", LIBDIR "extfs/extfs.ini");
  1188.     char *key, *value;
  1189.     char *p, *q, *r, c;
  1190.     
  1191.     extfs_no = 0;
  1192.     for (extfs_no = 0; keys != NULL && extfs_no < MAXEXTFS; extfs_no++) {
  1193.     keys = profile_iterator_next (keys, &key, &value);
  1194.     value = strdup (value);
  1195.  
  1196.     /* Handle those with a trailing ':', those flag that the
  1197.      * file system does not require an archive to work
  1198.      */
  1199.     
  1200.     if (key [strlen (key)-1] == ':'){
  1201.         key [strlen (key)-1] = 0;
  1202.         extfs_need_archive [extfs_no] = 0;
  1203.     } else
  1204.         extfs_need_archive [extfs_no] = 1;
  1205.     
  1206.     extfs_prefixes [extfs_no] = strdup (key);
  1207.     p = xmalloc (strlen (value) + 2, "Extfs: extensions");
  1208.     extfs_extensions [extfs_no] = p;
  1209.     q = value;
  1210.     for (c = *q; c; q = r + 1) {
  1211.         while (*q && *q <= 32) q++;
  1212.         for (r = q; *r > 32; r++);
  1213.         c = *r;
  1214.         *r = 0;
  1215.         if (*q) {
  1216.             strcpy (p, q);
  1217.             p = strchr (p, 0) + 1;
  1218.         }
  1219.     }
  1220.     free (value);
  1221.     *p = 0;
  1222.     if (p == extfs_extensions [extfs_no]) {
  1223.         free (extfs_prefixes [extfs_no]);
  1224.         free (extfs_extensions [extfs_no]);
  1225.         extfs_no--;
  1226.     }
  1227.     }
  1228.     free_profile_name (LIBDIR "extfs/extfs.ini");
  1229. }
  1230.  
  1231. int extfs_prefix_to_type (char *path)
  1232. {
  1233.     int i, j;
  1234.  
  1235.     for (i = 0; i < extfs_no; i++) {
  1236.         j = strlen (extfs_prefixes [i]);
  1237.         if (!strncmp (path, extfs_prefixes [i], j) && path [j] == ':')
  1238.             return i;
  1239.     } 
  1240.     return -1;
  1241. }
  1242.  
  1243. char *extfs_get_prefix (int idx)
  1244. {
  1245.     return extfs_prefixes [idx];
  1246. }
  1247.